实现桌面动态壁纸(一)

您所在的位置:网站首页 wallpaper engine桌面窗口管理器 实现桌面动态壁纸(一)

实现桌面动态壁纸(一)

2024-07-14 07:46| 来源: 网络整理| 查看: 265

目录

一、前言

二、有哪些基础需要我掌握的?

1. Windows Aero 与桌面窗口管理器(DWM)

1.1 Windows Aero 配色方案

1.2 桌面窗口管理器(DWM)

1.3 DWM 服务(仅限 Win7)

1.4 通过接口启用/禁用 DWM 窗口合成

1.5 DWM 系统关键进程

2. 通过 Spy++ 工具研究桌面窗口层次

2.1 桌面窗口层次

2.2 如何形成 WorkerW 分层窗口

三、实战&编写代码

1. 如何为嵌入桌面管理层窗口做好准备?

1.1. 确保已经启动 DWM 窗口合成

1.2. 怎样启用工作区窗口(WorkerW)?

1.3 为什么说高版本必须以 WorkerW 2 为父窗口呢?

1.4 如何获取这些窗口的句柄?

2. 更改父窗口实现嵌入壁纸窗口

2.1 问题一解决方案(更新父窗口问题)

2.2 问题二解决方案(虚拟桌面和扩展屏分辨率问题)

2.3 如何获取并判断系统版本(未来可能更改)

2.4 解决退出程序时窗口残影问题

2.5 上文注意事项的一些补充

3 关于 0x052C 私有消息的兼容性问题(重要)

3.1 0x052C 处理机制的潜在兼容性问题

3.2 对已知兼容性问题的解决方案

4. 完整代码以及测试截图

4.1 代码

4.2 运行结果展示

5. 关于后续工作

四、处理向后兼容性问题(6 月 25 日更新)

4.1 问题提出

4.2 窗口显示解决方案(3 月 24 日)

4.3 最新的窗口显示解决方案(6 月 9 日)

4.4 窗口显示解决方案(6 月 11 日 | 实验性)

五、后记

本文链接:[https://blog.csdn.net/qq_59075481/article/details/125361650]。

一、前言

本文章以在 Windows 桌面管理层窗口(桌面图标后面)嵌入第三方窗口为主题,主要针对动态壁纸实现原理进行讲解。

提醒:作者将对使用本文方法(也是网上流传很广的方法)遇到的一些问题进行仔细研究分析,文章将不定期更新,目前遇到的一些问题在 3.3(3.1, 3.2) 小节和 3.4(4.1) 小节均有描述。(2024.06.04)

!!!!本文有好多地方需要修订,未来可能重新写这篇(06.10)!!!

Wallpaper Engine 是由 Kristjan Skutta 所开发的一款动态壁纸软件,区别于其他形式的壁纸软件,Wallpaper Engine 可以让用户通过其引擎深度的自定义或编辑来创作出符合个人需求的壁纸样式。如果你惊艳于 WallpaperEngine 的效果,并且自己也想制作个性化的桌面美化程序,那么请跟我一道学习相关理论和技术实现。

系列文章:

序号文章标题(链接)AID1实现桌面动态壁纸(一)1253616502实现桌面动态壁纸(二)1338014913实现桌面动态壁纸(三)[未来发布]----4实现桌面动态壁纸——认识 WebView2 控件138637909 二、有哪些基础需要我掌握的? 1. Windows Aero 与桌面窗口管理器(DWM) 1.1 Windows Aero 配色方案

Windows Aero 是从 Windows Vista 开始使用的新型用户界面,透明玻璃感让用户一眼贯穿。“Aero”为四个英文单字的首字母缩略字:Authentic(真实)、Energetic(动感)、Reflective(反射)及 Open(开阔)。意为 Aero 界面是具立体感、令人震撼、具透视感和阔大的用户界面。除了透明的接口外,Windows Aero 也包含了实时缩略图、实时动画等窗口特效,吸引用户的目光。

Windows Aero 是 Windows Vista 开始使用的新元素,包含重新设计 Windows Explorer 样式、Windows Aero 玻璃样式、Windows Flip 3D 窗口切换、以及实时缩略图还有新的字体。

Windows 7 所使用的 Windows Aero 有许多功能上的调整,以及新的触控接口和新的视觉效果及特效:

Aero Peek:鼠标指针指向任务栏上图标,便会跳出该程序的缩略图预览,指向缩略图时还可看到该程序的全屏预览。此外,鼠标指向任务栏最右端的小按钮可看到桌面的预览。

Aero Shake:点击某一窗口后,摇一下鼠标,可让其他打开中的窗口缩到最小,再晃动一次便可恢撤消貌。

Aero Snap:点击窗口后并拖曳至桌面的左右边框,窗口变会填满该侧桌面的半部。拖曳至桌面上缘,窗口变会放到最大。此外,点击窗口的边框并拖曳至桌面上缘或下缘会使得窗口垂直放到最大,但宽度不变,逆向操作后窗口则会撤消回原貌。

触控接口:为了方便利用触控技术操作,些微放大了标题栏及任务栏的按钮。

放到最大的窗口仍旧保持透明的边框,而以往 Windows Vista 中,窗口放到最大后,会以该主题的颜色(窗口颜色)填满边框,有相当大的不同。

当鼠标滑过任务栏上的图标时,图标背景会浮现该图标最显著的 RGB 色彩,此外,鼠标的指针处会有更亮的颜色跟着指针移动。

当移动窗口时,Aero 特效的窗口更新率会降低,减低 CPU 和 GPU 的负荷, 让程序能稳定的运作。

用户可选择打开或关闭窗口边框阴影效果。

注:Windows7 Home Basic、Windows Vista Home Basic 隐藏了 Aero 特效,但可以通过修改注册表强行开启。Windows 8 正式版中取消了对系统窗口应用的 Aero Glass 特效。

1.2 桌面窗口管理器(DWM)

​桌面窗口管理器(Desktop Window Manager, DWM)是 Windows Vista 及更高版本的 Windows 桌面操作系统中的一个重要组件。

DWM 的桌面合成建立在 Composition 引擎基础之上,结合了 WPF 核心图形层组件基础。它的出现几乎改变了 Vista 中应用程序的屏幕象素显示方式。启用 DWM 后,提供的视觉效果有毛玻璃框架、3D 窗口变换动画、窗口翻转和高分辨率支持。

应用程序的显示不再是直接画到屏幕上,而是一个显示内存中的一个离屏 Surface 。然后由 DWM 将这些 Surface 合成显示到屏幕之上。

在Vista之前,Windows 要求应用程序画自己的可见区域,它们可以直接画在显卡的视频缓冲里面。而在 Vista,系统要求应用程序把整个表面画到离屏 Surface 当中。然后由 DWM 控制所有的离屏表面,并把它们合成到一起显示到真正的屏幕上。

DWM 的主要目标就是利用图形芯片的处理能力也给非游戏用户带来尽可能好的体验。因此 DWM 是基于 DirectX,特别是 Direct3D。更准确来说,DWM 是直接建立在一个称为 Milcore 的层次之上,而 Milcore 又建立在 DirectX 之上,最终是用 Direct3D 纹理来表示窗口内容和窗口框架。 DWM/Milcore 调用适当的 Direct3D 函数把所有的 Direct3D 纹理合成为最终的桌面。Vista 或 Win7 桌面就可以理解为一个全屏幕的 Direct3D 应用程序。

1.3 DWM 服务(仅限 Win7)

在 Win 7 上,桌面窗口管理器器,Desktop Window Manager Session Manager(DWMSMs)是一项服务,默认自动启动,若终止该服务,将导致 Aero 视觉效果消失。你可以在控制面板\管理工具\服务中查看服务状态:

Win7 UxSms 服务的显示名称

右键选项卡,点击属性栏,可以看到它的服务名称:UxSms。

服务设置页

 DWM 的目录位置为:C:\Windows\System32 ,其目录结构为:

 DWM 进程为 svchost.exe 的子进程,使用 Process Explorer 查看进程关系如下:

用户可以通过 NET 命令来操作 DWM 服务

控制台窗口键入以下指令:

1.启动 DWM 服务:net start UxSms

2.终止 DWM 服务:net stop UxSms

通过命令控制 DWM 服务进程 1.4 通过接口启用/禁用 DWM 窗口合成

Windows 7 以及 Vista 系统版本下,支持编程禁用 DWM,就是因为这个禁用功能导致我们的动态壁纸程序必须能够检测 DWM 的状态改变,除非程序不需要适配 Win7 系统。

完全控制桌面的应用程序(例如在全屏模式下运行的游戏)必须确定是否已启用 DWM,需要两个函数来检查 DWM 状态(下面函数仅在 Win7 有效):

DwmIsCompositionEnabledDwmEnableComposition

在 fEnable 设置为 DWM_EC_DISABLECOMPOSITION 的情况下调用 DwmEnableComposition 会禁用 DWM 合成,直到调用进程已关闭,或者通过将 fEnable 设置为 DWM_EC_ENABLECOMPOSITION 调用 DwmEnableComposition 来重新启用合成。

注意:禁用组合的所有应用程序关闭后或通过调用 DwmEnableComposition 手动重新启用组合后,DWM 组合会自动重启。

1.5 DWM 系统关键进程

从 Windows 8 开始,当应用程序尝试直接绘制到主要显示图面时,DWM 会自动禁用合成。 组合将被禁用,直到该应用程序释放主设备图面。微软将该进程归为由 Winlogon.exe 进程启动和监视的关键进程,dwm.exe 不再是服务进程,不可以被编程终止,否则会黑屏卡死。主要原因是很多系统运行中的应用,尤其是 UWP 应用均依赖 DWM,如果 DWM 崩溃则它们的图形界面也将崩溃。

但是,有很多通过钩子或者修改链接库实现禁用 DWM 的例子,如果有空我会单独写一期。

2. 通过 Spy++ 工具研究桌面窗口层次 2.1 桌面窗口层次

Windows 的桌面由图标列表和背景窗口等组成,这些窗口以及控件窗口之间具有一定的层次。使用 Spy++ 可以获取到开机后普通的桌面窗口层次,结构如下所示:

"Program Manager" Progman

| -- "" SHELLDLL_DefView

     | -- "FolderView" SysListView32

          | -- "" SysHeader32    (Unvisible)

可以观察到在 Desktop 窗口中 Z-Order 位于最底层的窗口是 Progman 窗口, 其子窗口是 SHELLDLL_DefView 窗口,SHELLDLL_DefView 又有一个窗口类为 SysListView32 的子窗口最后 SysHeader32 窗口是不可见的。显而易见,桌面上的图标都在名为 SysListView32 的列表窗口中。如果熟悉 MFC,看到 SysListView32 会很眼熟,MFC中的 CListCtrl 控件窗口类也是SysListView32。

在这种层次下, 往 Progman 窗口中嵌入一个 WM_CHILDWINDOW 样式的窗口,将会覆盖在 SysLisView32 窗口上方,或者被前面的窗口挡住,无法通过嵌入窗口的方式实现类似 WallPaper Engine 那样的壁纸。我们现在看下 Wallpaper Engine 嵌入壁纸窗口时候桌面窗口层次,Wallpaper Engine 在 Win 7 上的行为和更高版本系统不一样。

首先是 Win8 / 8.1 系统:

"" WorkerW    (本文称作 WorkerW 1)

| -- "" WorkerW (WorkerW 0,顶级窗口为 Progman 时就默认具有)

| -- "" SHELLDLL_DefView

     | -- "FolderView" SysListView32

          | -- "" SysHeader32    (Unvisible)

"" WorkerW    (本文称作 WorkerW 2)

| -- "" CefBrowserWindow    (WallpaperEngine 的浏览器窗口)

"Program Manager" Progman

其次, Win 10 至 Win 11 23H2 操作系统:

"" WorkerW    (本文称作 WorkerW 1)

| -- "" SHELLDLL_DefView

     | -- "FolderView" SysListView32

          | -- "" SysHeader32    (Unvisible)

"" WorkerW    (本文称作 WorkerW 2)

| -- "" CefBrowserWindow    (WallpaperEngine 的浏览器窗口)

"Program Manager" Progman

然后,是 Win 7 系统,层次结构如下:

"" WorkerW 1    (Visible, Aero)

| -- "" SHELLDLL_DefView

     | -- "FolderView" SysListView32

          | -- "" SysHeader32    (Unvisible)

"" WorkerW 2    (Unvisible, White)

"Program Manager" Progman

| -- "" CefBrowserWindow    (Wallpaper Engine 的浏览器窗口)

最后,Win Vista 似乎无法通过发送消息产生类似的窗口层次。

我们发现 SHELLDLL_DefView 及其下面的桌面图标窗口成为一个 WorkerW 窗口的子窗口(我们称 WorkerW 1),和第一个 WorkerW 同级但 Z 序位于下方的 WorkerW 窗口(我们称 WorkerW 2),在Win 8至 Win 11上壁纸窗口设为了 WokerW 2 的子窗口,而在 Win 7 上则设置为 Progman 的子窗口。

从 Spy ++ 返回的信息来看,WorkerW 和 Progman 都是 NULL,也就是说它们是桌面顶级窗口,没有父窗口和所有者窗口。

在 Win 7 下用 Spy++ 分别看 WorkerW 1、WorkerW 2 的窗口样式,会发现 WorkerW 2 是一个 Popup 窗口,其 Parent 是 Progman 窗口 ,其上一个窗口句柄是 WorkerW 1。 WokerW 1 窗口也是一个Popup 窗口,其父窗口显示无,但是其下一个窗口显示的句柄正好是 WokerW 2。此时这三个窗口Z序很明显了:WorkerW1 > WorkerW2 > Progman 窗口。

在分析窗口样式的时候我还发现一个有趣的现象:

比如在 Win 11 下,WorkerW 2 的扩展样式中有一个叫 WS_EX_TRANSPARENT 的窗口样式,而在 WorkerW 1 下则没有:

Win 11 WorkerW 1 窗口样式 Win 11 WorkerW 2 窗口样式

对于 WS_EX_TRANSPARENT 样式,MSDN 是这样说的, 在窗口下方(由同一个线程创建)的兄弟窗口被绘制之前,不应该对窗口进行绘制。窗口显示为透明,因为底层兄弟窗口的位已经被绘制。要在没有这些限制的情况下实现透明度,请使用 SetWindowRgn 函数。

也就是说这个扩展样式,可以实现鼠标穿透(窗口的穿透性质和透明度之间的关系可以看 文章)。

再看看 Win 7 下的窗口样式:

可以看到 WorkerW 不仅有鼠标穿透,还有 WS_EX_LAYERED 分层窗口样式。用 GetLayeredWindowAttributes 函数检索透明度时候调用失败,猜测窗口是使用UpdateLayeredWindow 实现透明度的。但是Win 7/8上,WorkerW 1 并不是透明的,会遮挡 WorkerW 2,在 Win 8.1 及以上则不遮挡,单纯从窗口样式上很难判断窗口是否透明。

WorkerW 1、WorkerW 2 和 Progman 窗口一样都属于 explorer.exe 进程的窗口。实际上想要将自己的窗口嵌入到 Windows 桌面图标下方,桌面的窗口层次一定要正确,并且要保证高 Z 序窗口是透明的。总结以上分析,Win 7/8 窗口应嵌入 Progman 并且隐藏 WorkerW 2;Win 8.1开始的系统上,窗口应嵌入 WorkerW 2 。需要注意的是,用嵌入窗口的方式实现动态壁纸,只能在 WIn7 及其以上系统上实现,Vista/XP 以及更早的系统无法产生这种透明的窗口层次, XP是没有 DWM 框架且窗口不透明,Vista 是早期的 DWM 有些功能不支持,导致无法用 Worker 分层窗口嵌入壁纸窗口。

2.2 如何形成 WorkerW 分层窗口

WorkerW 分层窗口用于在切换桌面时产生淡入淡出动画,这主要通过在名为 WorkerW 的平滑移动窗口上绘制桌面 Progman 窗口的 HDC 信息得到。这种窗口是延时产生的,最典型的是在 Win 7 SP3 上更改显示器配色方案时 以及 Win 10 等打开“任务视图”时。下面演示两种情况下窗口层次是如何产生的。

(1)在 Win 7 SP3 上更改显示器配色方案

使用 Spy ++ 监视窗口消息,当更改显示器配色方案时,系统会调用 PostMessage 函数,并发送一条未公开的 WindowsMessage,即 0x052C (WM_SHELLPARENTCHANGING)

Windows Basic Windows Aero Aero Activated

(2)在 Win 11 上监视 Progman,并打开和关闭“任务视图”按钮

可以观察到,打开“任务视图”的时候,wParam 是 0x0D,lParam 是 0x01,关闭时则是 0x0.

目前看来,在不同操作系统上 Param 参数的功能不太一致,这需要更为详细的研究。

建议使用具有超时发送的 SendMessageTimeout 函数,并在指定的系统版本上使用以下建议的参数数值发送消息:

// 下面列出适用于不同系统的消息参数格式: // Win7, 8, 8.1, Win 10 几乎全部版本,Win 11 21H2, 22H2, 23H2 全部已发布版本 SendMessageTimeoutW(hProgmanWnd, 0x052C, 0, 0, SMTO_NORMAL, 0x03E8, &result); // 部分 Win10 预览体验计划版本,Win 11 24H2 Dev 渠道 SendMessageTimeoutW(hProgmanWnd, 0x052C, 0xD, 1, SMTO_NORMAL, 0x03E8, &result);

其中,前 4 个参数和 SendMessage 相同,后面参数是控制超时的,0x03E8 等同于十进制的1000,表示等待超时时间是1000毫秒。

三、实战&编写代码 1. 如何为嵌入桌面管理层窗口做好准备?

只要能将我们自己的窗口嵌入到 Windows 图标下面并且可视,我们就可以在窗口上引入动画了!

1.1. 确保已经启动 DWM 窗口合成

2024.06.06:最新研究发现,只通过 DwmIsCompositionEnabled 函数和服务进程状态来检查是否启用了窗口合成是不精确的,当在使用类似 Basic 主题(通过一个没有文档化的 API 操作了 DWM 内部数据)或者在注册表和组策略中关闭了窗口合成,将对使用该 API 的简单检测产生逃逸,导致 0x052C 不能正常产生动作。作者将在未来更新此部分的环境检测策略。

想让自己的窗口嵌入到桌面管理层窗口下面不被遮挡,你必须让桌面管理层窗口变成透明。说到窗口透明,做过客户端的程序员可能会想到 Windows 的 Layerd 窗口,但是实际上用 Spy ++ 去看 WorkerW 1 窗口样式, 他并不包含 WS_EX_LAYERD 风格。而且有个重要的问题,如果 Layerd 窗口采用 SetLayeredWindowAttributes 设置为全透明窗口,则其子窗口也会不可见。此外,使用 UpdateLayerdWindow 实现的透明窗口,完全透明的地方鼠标将会穿透。而用 Spy++ 查看桌面的 SysListView32 窗口,它是可以收到各种鼠标消息。那么,将原本 Progman 的子窗口 SHELLDLL_DefView(拥有类名为 SysListView32 的桌面管理层图标窗口)变成 WorkerW 1 的子窗口,并且让WorkerW 1 中除了桌面图标部分其他地方都是透明的,这个是怎么实现的呢?实际上 WorkerW 1 窗口这种透明效果是由 DWM( Desktop Window Manager) 来控制的(如何实现透明后文会说)。

Desktop Window Manager,它是 Vista 之后才出现的一个新的系统组件,它的进程名是dwm.exe。在 Win8 及以上系统,它会随系统自动启动, 并且一直运行。在 Vista/Win7 系统中,一般我们在使用 Aero 主题的时候才会启动这个服务。操作系统提供了 Desktop Window Manager 相关的 API ,相关接口都在 dwmapi.dll 中。DWM API 允许我们设置窗体在与其他窗体组合/重叠时候的显示特效,如全透明、半透明、模糊等效果。

所以回到在桌面管理层窗口嵌入窗口的问题,在 Win 7/8 上,我们首先要判断 DWM Compositon 是否开启,如果被禁止,则桌面管理层窗口层次是不满足要求的,DWM API 提供 DwmIsCompositionEnabled 函数来判断 DWM Composition 是否启用:

BOOL IsDwmCompositionEnabled() { // 注意这DWM API在Vista/Win7系统以上才有的 // win8/win10是不需要判断的会一直返回TRUE BOOL bEnabled = FALSE; typedef HRESULT(__stdcall *fnDwmIsCompositionEnabled)(BOOL* pfEnabled); HMODULE hModuleDwm = LoadLibraryA("dwmapi.dll"); if (hModuleDwm != 0) { fnDwmIsCompositionEnabled pFunc = (fnDwmIsCompositionEnabled)GetProcAddress(hModuleDwm, "DwmIsCompositionEnabled"); if (pFunc != 0) { BOOL result = FALSE; if (pFunc(&result) == S_OK) { bEnabled = result; } } else { SetLastError(ERROR_ACCESS_DENIED); bEnabled = TRUE; } FreeLibrary(hModuleDwm); hModuleDwm = 0; } return bEnabled; }

如果 DWM Composition 未启用可以使用 DwmEnableComposition 启用它,这个函数参数有两个选择,DWM_EC_ENABLECOMPOSITION,Win 7 下将启用默认的Aero 主题;DWM_EC_DISABLECOMPOSITION,Win 7 下将启用Windows 7 Basic 主题。

样例代码:

/* HRESULT DwmEnableComposition( UINT uCompositionAction ); */ #include #pragma comment(lib, "dwmapi.lib") // S1: ... HRESULT hr_db = S_OK; // Disable DWM Composition hr_db = DwmEnableComposition(DWM_EC_DISABLECOMPOSITION); if (SUCCEEDED(hr_db)) { // ... } ... // S2: ... HRESULT hr_eb = S_OK; // Enable DWM Composition hr_eb = DwmEnableComposition(DWM_EC_ENABLECOMPOSITION); if (SUCCEEDED(hr_eb)) { // ... } ...

但是,在实际测试过程中发现,如果 Uxsms 服务没有启动,则 DwmEnableComposition 始终失败,如果 dwm.exe 进程崩溃,但 Uxsms 服务可能依然正常运行,这说明 Uxsms 服务相当于加载器,我们需要在调用窗口合成之前检查 dwm.exe 是否正在运行,如果没有运行就尝试重启 Uxsms 服务,然后再调用启用窗口合成 的函数。

DWORD FindProcessIDByName(LPCWSTR processName) { DWORD processId = 0; HANDLE hProcessSnap; PROCESSENTRY32W pe32{}; pe32.dwSize = sizeof(PROCESSENTRY32W); hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnap == INVALID_HANDLE_VALUE) { return(0); } if (!Process32FirstW(hProcessSnap, &pe32)) { CloseHandle(hProcessSnap); // clean the snapshot object return(0); } do { if (!wcscmp(pe32.szExeFile, processName))//进程名称 { processId = pe32.th32ProcessID;//进程ID break; } } while (Process32NextW(hProcessSnap, &pe32)); CloseHandle(hProcessSnap); return processId; } BOOL QueryEnableDwmComposition() { // 注意 DWM API 在 Vista/Win7 系统以上才有 // win8 / win10 是不需要判断的会一直返回 TRUE BOOL bEnabled = FALSE; typedef HRESULT(__stdcall* fnDwmIsCompositionEnabled)(BOOL* pfEnabled); typedef HRESULT(__stdcall* fnDwmEnableComposition)(UINT uCompositionAction); HMODULE hModuleDwm = LoadLibraryA("dwmapi.dll"); if (hModuleDwm != 0) { auto pFuncIsEnabled = (fnDwmIsCompositionEnabled)GetProcAddress( hModuleDwm, "DwmIsCompositionEnabled"); auto pFuncEnableDwm = (fnDwmEnableComposition)GetProcAddress( hModuleDwm, "DwmEnableComposition"); if (pFuncIsEnabled != 0) { BOOL result = FALSE; if (pFuncIsEnabled(&result) == S_OK) { // 没有启动就启动一下 if(result == TRUE) bEnabled = TRUE; else if (pFuncEnableDwm != 0) { printf("[*] Attempt to start Dwm Service.\n"); if (!FindProcessIDByName(L"dwm.exe")) { system("SC stop UxSms"); WaitForSingleObject(GetCurrentProcess(), 1500); } system("SC start UxSms"); WaitForSingleObject(GetCurrentProcess(), 500); // #define DWM_EC_ENABLECOMPOSITION 1 if (pFuncEnableDwm(TRUE) == S_OK) { bEnabled = TRUE;// 判断启动是否成功 } else { SetLastError(ERROR_INTERNAL_ERROR); } } } } else { SetLastError(ERROR_ACCESS_DENIED); bEnabled = TRUE; } FreeLibrary(hModuleDwm); hModuleDwm = 0; } return bEnabled; }

更好的方法是使用 SCM 配置函数监视和维持 UxSms 服务进程的启动状态。这里懒,就用 sc 命令代替了。

测试效果:

2024/07/09 更新:使用服务控制器 API 实现的状态检查:

// 检查服务状态 SERVICE_STATUS_PROCESS QueryServiceStatus(const WCHAR* serviceName) { SERVICE_STATUS_PROCESS ssp = {}; SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (hSCManager == NULL) { std::wcerr


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3